package com.demo.config;

import com.demo.config.filter.TokenFilter;
import com.demo.config.handler.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;


import java.util.List;

@Configuration
public class SecurityConfig {
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;
    @Autowired
    private TokenFilter tokenFilter;
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                // 表单设置
                .formLogin((formLogin)->{
                    formLogin.usernameParameter("loginAct") // 用户名参数
                            .passwordParameter("loginPwd") //密码参数
                            .loginProcessingUrl("/api/doLogin") //处理登录请求地址
                            .loginPage("/api/toLogin") // 登录页面地址
                            .successHandler(myAuthenticationSuccessHandler) //登录成功处理器
                            .failureHandler(myAuthenticationFailureHandler); // 登录失败处理器
                })
                // 异常处理器
                .exceptionHandling((exceptionHandling)->{
                    exceptionHandling
                            .authenticationEntryPoint(myAuthenticationEntryPoint) //（401：未授权，请求未携带token）
                            .accessDeniedHandler(myAccessDeniedHandler);// 403 异常处理器 ：请求携带token，但权限不足
                })
                // 其他请求设置
                .authorizeHttpRequests((authorize)->{
                    authorize.anyRequest().authenticated(); // 所有请求都需要认证
                })
                // 登出测试
                .logout((logout)->{
                    logout.logoutUrl("/api/logout") // 处理登出请求地址
                            .logoutSuccessHandler(myLogoutSuccessHandler); // 登出成功处理器
                })
                // 关闭跨站请求伪造
                .csrf((csrf)->{
                    csrf.disable();
                })
                // 关闭session使用策略
                .sessionManagement((sessionManagement)->{
                    sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                })
                // 登录之后要进行的处理
                .addFilterAfter(tokenFilter, UsernamePasswordAuthenticationFilter.class)
                .cors((cors)->{
                    cors.configurationSource(corsConfigurationSource()); // 指定 CORS配置源
                })
                .build();
    }

    /**
     * 跨域配置，允许所有来源，所有方法，所有头信息，允许发送Cookie
     * 配置来源（通义千问：security6中配置访问允许跨域请求）
     * @return
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("*")); // 允许所有来源
        configuration.setAllowedMethods(List.of("GET","POST","DELETE","PUT","OPTIONS")); // 允许的方法
        configuration.setAllowedHeaders(List.of("*")); // 允许的头信息
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        /*
         * source.registerCorsConfiguration()方法用于将特定的 CORS 配置应用到指定的 URL 路径模式。
         * 这个方法允许你为不同的 URL 路径设置不同的 CORS 策略，从而实现更细粒度的控制
         * */
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}

